﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Red.Core.DataConsistency
{
    /// <summary>
    /// Object for which the changes should be tracked globally
    /// </summary>
    public class ConsistentObject
    {
        /// <summary>
        /// Runtime only "consistency" ID - incremented after every operation with the object
        /// </summary>
        public UInt64 ConsistencyID { get; private set; }

        /// <summary>
        /// Internal tracking ID, used only for objects that are tracked through the StateGroups
        /// Objects are NEVER unregistered from the system, the groups are. This ID is only used to ensure proper signal routing.
        /// </summary>
        private UInt64 _TrackingID;

        /// <summary>
        /// Internal random number generator
        /// </summary>
        private static readonly Random _RandomGenerator = new Random(12345);

        /// <summary>
        /// Initialize
        /// </summary>
        public ConsistentObject()
        {
            // generate pseudo-random consistency ID for each object
            var initData = new byte[8];
            _RandomGenerator.NextBytes(initData);
            ConsistencyID = BitConverter.ToUInt64(initData, 0);

            // object is not tracked by default
            _TrackingID = 0;
        }

        /// <summary>
        /// Mark this property as changed so any cached values will have to be refreshed
        /// This is an important part of trying to keep the data consistent between the in-memory storage and the visualization
        /// Note that the consistency marker only goes up, even in the Undo operations
        /// </summary>
        protected void InvalidateConsistencyMarker()
        {
            // internal consistency state changed, all lazy tokens will have to be reevaluated
            ConsistencyID += 1;

            // update active tracking (will cause callbacks to be fired)
            if (_TrackingID != 0)
            {
                StateSynchronizer.TheInstance.SignalObject(_TrackingID);
            }
        }

        /// <summary>
        /// Track changes of this object within given group
        /// Any modifications to this object will trigger the group OnStateChanged event
        /// NOTE: no callbacks are not fired immediately. Instead, they only fire when the StateSynchronizer is flushed.
        /// </summary>
        /// <param name="group">Group that wished to track this object</param>
        public void TrackChanges(UInt32 groupId, UInt32 groupGeneration)
        {
            /// track the object, will update the tracking ID if needed (we don't care here, it's internal data of the StateSynchronizer any way)
            StateSynchronizer.TheInstance.TrackObject( ref _TrackingID, groupId, groupGeneration);
        }
    }
}
